package com.yourenbang.lingyun.transaction.web.service.impl.fund.fundopt;

import com.yourenbang.lingyun.constants.BizCodeMessages;
import com.yourenbang.lingyun.constants.enums.ChannelIdEnums;
import com.yourenbang.lingyun.constants.enums.FundBillType;
import com.yourenbang.lingyun.constants.enums.fund.AccountMainJournalType;
import com.yourenbang.lingyun.constants.enums.fund.AccountType;
import com.yourenbang.lingyun.core.exception.BizRuntimeException;
import com.yourenbang.lingyun.core.exception.UserBizException;
import com.yourenbang.lingyun.core.message.Result;
import com.yourenbang.lingyun.core.util.BizAssertUtils;
import com.yourenbang.lingyun.core.util.KLog;
import com.yourenbang.lingyun.domain.FundAccount;
import com.yourenbang.lingyun.mapper.FundAccountMapper;
import com.yourenbang.lingyun.transaction.constant.ChangePlanStatus;
import com.yourenbang.lingyun.transaction.constant.JournalStatus;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountChangePlanMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountJournalMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundBillMapper;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountChangePlan;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountJournal;
import com.yourenbang.lingyun.transaction.web.domain.vo.JournalInfoVO;
import com.yourenbang.lingyun.transaction.web.service.fund.fundopt.FundOptCancelService;
import com.yourenbang.transaction.api.model.dto.AddBillDTO;
import com.yourenbang.transaction.api.service.fund.FundBillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Objects;

@Slf4j
@Service
public class FundOptCancelServiceImpl implements FundOptCancelService {
    @Resource
    private TransactionFundAccountMapper transactionFundAccountMapper;
    @Resource
    private FundAccountMapper fundAccountMapper;
    @Resource
    private FundBillService fundBillService;
    @Resource
    private TransactionFundBillMapper fundBillMapper;
    @Resource
    private TransactionFundAccountChangePlanMapper fundAccountChangePlanMapper;
    @Resource
    private TransactionFundAccountJournalMapper fundAccountJournalMapper;

    @Override
    public Result cancel(FundAccountChangePlan fundAccountChangePlan) {

        // 仅针对正在发生变更、等待发生变更对资金变动,如果资金已经发生变动,需要使用减帐完成
        if (!(fundAccountChangePlan.getAccountChangePlanStatus() == ChangePlanStatus.CHANGING ||
                fundAccountChangePlan.getAccountChangePlanStatus() == ChangePlanStatus.WAIT_TO_CHANGE)
        ) {
            throw new BizRuntimeException(BizCodeMessages.TRANSACTION_CHANGE_PLAN_ERROR, "正在变更获等待变更的资金计划才能撤销");
        }

        //用户账户
        FundAccount toFundAccount = fundAccountMapper.selectByPrimaryKey(fundAccountChangePlan.getToFundAccountId());
        if (Objects.isNull(toFundAccount)) {
            throw new BizRuntimeException(BizCodeMessages.TRANSACTION_FUNDACCOUNT_NONE);
        }

        //判断重复归还
        int readyreturnFund = fundBillMapper.countReadyreturnFund(fundAccountChangePlan.getPlatNo(),
                fundAccountChangePlan.getOrderId());
        if (readyreturnFund > 0) {
            KLog.warn("【提现】归还冻结金额, 已归还, 无需重复归还, 资金变动记录:{}", fundAccountChangePlan.toString());
            throw new BizRuntimeException(BizCodeMessages.TRANSACTION_CHANGE_PLAN_REPEAT_CANCEL);
        }

        //资金发起账户
        FundAccount fromFundAccount = fundAccountMapper.selectByPrimaryKey(fundAccountChangePlan.getFundAccountId());
        if (Objects.isNull(fromFundAccount)) {
            throw new BizRuntimeException(BizCodeMessages.TRANSACTION_FUNDACCOUNT_NONE);
        }


        if (!(AccountType.SYS_V2_WITHDRAW.isEqualsFullAccountNo(fromFundAccount.getAccountType())
                || AccountType.SYS_BOSS_WITHDRAW.isEqualsFullAccountNo(fromFundAccount.getAccountType()))
        ) {

            // 出款只有提现账号才能退款
            throw new UserBizException(BizCodeMessages.PARAMS_ERROR, "资金出款账户类型异常");
        }

        AccountMainJournalType accountMainJournalType;
        if (AccountType.ALLY_V2.isEqualsFullAccountNo(toFundAccount.getAccountType())
                || AccountType.ALLY_FROM_BOSS.isEqualsFullAccountNo(toFundAccount.getAccountType())
        ) {

            // 接收方为盟友资金
            accountMainJournalType = AccountMainJournalType.ALLY_REFUND;

        } else if (AccountType.MER.isEqualsFullAccountNo(toFundAccount.getAccountType())) {

            // 接收方为商户资金
            accountMainJournalType = AccountMainJournalType.MERCHANT_REFUND;

        } else {
            throw new UserBizException(BizCodeMessages.PARAMS_ERROR, "资金接收账户类型异常");
        }

        //根据变动计划,从哪里来,返回哪里
        JournalInfoVO journalInfoVO = new JournalInfoVO(
                AccountType.getAccountTypeByFullCode(fromFundAccount.getAccountType()),
                AccountType.getAccountTypeByFullCode(toFundAccount.getAccountType()),
                accountMainJournalType
        );


        try {

            /*
             * !!!!!! 资金接收方、发起方,沿用资金计划时的资金发起关系
             */

            //资金接收方冻结撤销 可提现金额+;冻结中的金额-
            int i = transactionFundAccountMapper.frozenCancel(fundAccountChangePlan.getToFundAccountId(), fundAccountChangePlan.getChangeAmount());
            BizAssertUtils.assertOne(i, "资金接收方金额冻结撤销失败");

            //记录资金接收起方流水
            FundAccountJournal toJournal = getToFundAccountJournal(toFundAccount, journalInfoVO, fundAccountChangePlan);
            fundAccountJournalMapper.insert(toJournal);

            //资金发起方冻结撤销 可提现金额+;冻结中的金额-
            int y = transactionFundAccountMapper.frozenCancel(fundAccountChangePlan.getFundAccountId(), fundAccountChangePlan.getChangeAmount());
            BizAssertUtils.assertOne(y, "资金发起方金额冻结撤销失败");

            //记录资金发起方流水
            FundAccountJournal journal = getFromFundAccountJournal(fromFundAccount, journalInfoVO, fundAccountChangePlan);
            fundAccountJournalMapper.insert(journal);


            //资金变动撤销成功
            FundAccountChangePlan upChange = new FundAccountChangePlan(fundAccountChangePlan.getPlatNo());
            upChange.setFundAccountChangePlanId(fundAccountChangePlan.getFundAccountChangePlanId());
            upChange.setAccountChangePlanStatus(ChangePlanStatus.CANCEL);
            fundAccountChangePlanMapper.updateById(upChange);

        } catch (BizRuntimeException e) {

            KLog.warn("【提现】归还冻结金额,异常:{}, 资金变动记录:{}", e.getMessage(), fundAccountChangePlan.toString(), e);
            throw e;

        } catch (Exception e) {

            KLog.warn("【提现】归还冻结金额,异常:{}, 资金变动记录:{}", e.getMessage(), fundAccountChangePlan.toString(), e);
            throw e;

        }

        //账单
        AddBillDTO billDTO = AddBillDTO.builder()
                .userBaseId(toFundAccount.getUserBaseId())
                .fundAccountId(fundAccountChangePlan.getToFundAccountId())
                .fundAccountChangePlanId(fundAccountChangePlan.getFundAccountChangePlanId())
                .orderId(fundAccountChangePlan.getOrderId())
                .amount(fundAccountChangePlan.getChangeAmount())
                .billType(FundBillType.TYPE_201)
                .showFlag(true)
                .channelId(ChannelIdEnums.no.getCode())
                .platNo(fundAccountChangePlan.getPlatNo())
                .build();

        billDTO.setAccountMainJournalType(journalInfoVO.getAccountMainJournalType().getCode());
        billDTO.setAccountMainJournalTypeName(journalInfoVO.getAccountMainJournalType().getName());

        fundBillService.addBill(billDTO);

        return Result.builder().success();
    }

    @Override
    public Result cancelBalancePay(FundAccountChangePlan fundAccountChangePlan) {

        // 仅针对正在发生变更、等待发生变更对资金变动,如果资金已经发生变动,需要使用减帐完成
        if (!(fundAccountChangePlan.getAccountChangePlanStatus() == ChangePlanStatus.CHANGING ||
                fundAccountChangePlan.getAccountChangePlanStatus() == ChangePlanStatus.WAIT_TO_CHANGE)
        ) {
            throw new BizRuntimeException(BizCodeMessages.TRANSACTION_CHANGE_PLAN_ERROR, "正在变更获等待变更的资金计划才能撤销");
        }

        //资金接收账户(平台提现、boss提现账号)
        FundAccount toFundAccount = fundAccountMapper.selectByPrimaryKey(fundAccountChangePlan.getToFundAccountId());
        if (Objects.isNull(toFundAccount)) {
            throw new BizRuntimeException(BizCodeMessages.TRANSACTION_FUNDACCOUNT_NONE);
        }

        //判断重复归还
        int readyreturnFund = fundBillMapper.countReadyreturnFund(fundAccountChangePlan.getPlatNo(),
                fundAccountChangePlan.getOrderId());
        if (readyreturnFund > 0) {
            KLog.warn("【提现】归还冻结金额, 已归还, 无需重复归还, 资金变动记录:{}", fundAccountChangePlan.toString());
            throw new BizRuntimeException(BizCodeMessages.TRANSACTION_CHANGE_PLAN_REPEAT_CANCEL);
        }

        //资金发起账户
        FundAccount fromFundAccount = fundAccountMapper.selectByPrimaryKey(fundAccountChangePlan.getFundAccountId());
        if (Objects.isNull(fromFundAccount)) {
            throw new BizRuntimeException(BizCodeMessages.TRANSACTION_FUNDACCOUNT_NONE);
        }


        if (!(AccountType.SYS_V2_WITHDRAW.isEqualsFullAccountNo(toFundAccount.getAccountType())
                || AccountType.SYS_BOSS_WITHDRAW.isEqualsFullAccountNo(toFundAccount.getAccountType()))
        ) {

            // 余额支付收款账号只能是平台、boss资金账号
            throw new UserBizException(BizCodeMessages.PARAMS_ERROR, "资金出款账户类型异常");

        }

        AccountMainJournalType accountMainJournalType;
        if (AccountType.ALLY_V2.isEqualsFullAccountNo(fromFundAccount.getAccountType())
                || AccountType.ALLY_FROM_BOSS.isEqualsFullAccountNo(fromFundAccount.getAccountType())
        ) {

            // 接收方为平台、boss资金
            accountMainJournalType = AccountMainJournalType.ALLY_REFUND;

        } else {
            throw new UserBizException(BizCodeMessages.PARAMS_ERROR, "资金接收账户类型异常");
        }

        //根据变动计划,从哪里来,返回哪里
        JournalInfoVO journalInfoVO = new JournalInfoVO(
                AccountType.getAccountTypeByFullCode(fromFundAccount.getAccountType()),
                AccountType.getAccountTypeByFullCode(toFundAccount.getAccountType()),
                accountMainJournalType
        );


        try {

            /*
             * !!!!!! 资金接收方、发起方,沿用资金计划时的资金发起关系
             */

            //资金发起方冻结撤销 可提现金额+;冻结中的金额-
            int y = transactionFundAccountMapper.frozenCancel(fundAccountChangePlan.getFundAccountId(), fundAccountChangePlan.getChangeAmount());
            BizAssertUtils.assertOne(y, "资金发起方金额冻结撤销失败");

            //记录资金发起方流水
            FundAccountJournal journal = getFromFundAccountJournal(fromFundAccount, journalInfoVO, fundAccountChangePlan);
            fundAccountJournalMapper.insert(journal);


            //资金变动撤销成功
            FundAccountChangePlan upChange = new FundAccountChangePlan(fundAccountChangePlan.getPlatNo());
            upChange.setFundAccountChangePlanId(fundAccountChangePlan.getFundAccountChangePlanId());
            upChange.setAccountChangePlanStatus(ChangePlanStatus.CANCEL);
            fundAccountChangePlanMapper.updateById(upChange);

        } catch (BizRuntimeException e) {

            KLog.warn("【提现】归还冻结金额,异常:{}, 资金变动记录:{}", e.getMessage(), fundAccountChangePlan.toString(), e);
            throw e;

        } catch (Exception e) {

            KLog.warn("【提现】归还冻结金额,异常:{}, 资金变动记录:{}", e.getMessage(), fundAccountChangePlan.toString(), e);
            throw e;

        }

        //账单
        AddBillDTO billDTO = AddBillDTO.builder()
                .userBaseId(fromFundAccount.getUserBaseId())
                .fundAccountId(fundAccountChangePlan.getFundAccountId())
                .fundAccountChangePlanId(fundAccountChangePlan.getFundAccountChangePlanId())
                .orderId(fundAccountChangePlan.getOrderId())
                .amount(fundAccountChangePlan.getChangeAmount())
                .billType(FundBillType.TYPE_201)
                .showFlag(true)
                .channelId(ChannelIdEnums.no.getCode())
                .platNo(fundAccountChangePlan.getPlatNo())
                .build();

        billDTO.setAccountMainJournalType(journalInfoVO.getAccountMainJournalType().getCode());
        billDTO.setAccountMainJournalTypeName(journalInfoVO.getAccountMainJournalType().getName());

        fundBillService.addBill(billDTO);

        return Result.builder().success();
    }

    private FundAccountJournal getToFundAccountJournal(FundAccount userAccountByType, JournalInfoVO journalVO,
                                                       FundAccountChangePlan changePlan) {
        //资金接收方流水记录
        FundAccountJournal journalUser = new FundAccountJournal(changePlan.getPlatNo());
        journalUser.setChannelId(changePlan.getChannelId());
        journalUser.setFundAccountChangePlanId(changePlan.getFundAccountChangePlanId());
        journalUser.setFundAccountId(userAccountByType.getFundAccountId());
        journalUser.setJournalStatus(JournalStatus.SUCCESS);
        journalUser.setJournalChangeValue(changePlan.getChangeAmount());
        journalUser.setAccountJournalAcid(changePlan.getOrderId());

        journalUser.setAccountMainJournalType(journalVO.getAccountMainJournalType().getCode());
        journalUser.setAccountMainJournalTypeName(journalVO.getAccountMainJournalType().getName());

        journalUser.setAccountIncome(BigDecimal.ZERO);            //收入资金变动金额
        journalUser.setAccountAmountFrozen(changePlan.getChangeAmount());   //冻结资金变动收入
        journalUser.setAccountAmountWithdrawable(changePlan.getChangeAmount());    //可提现资金变动收入
        journalUser.setAccountAmountWithdrawed(BigDecimal.ZERO);  //已提现资金变动收入
        journalUser.setAccountAmountUndepositable(BigDecimal.ZERO);   //不可提现资金变动收入

        journalUser.setAccountIncomeBefore(userAccountByType.getAccountIncome());      //收入变动前的金额
        journalUser.setAccountIncomeAfterwards(userAccountByType.getAccountIncome());  //收入变动后的金额

        journalUser.setAccountAmountFrozenBefore(userAccountByType.getAccountAmountFrozen());   //冻结资金变化前金额
        journalUser.setAccountAmountFrozenAfterwards(userAccountByType.getAccountAmountFrozen().subtract(changePlan.getChangeAmount())); //冻结资金变化后金额

        journalUser.setAccountAmountWithdrawableBefore(userAccountByType.getAccountAmountWithdrawable());   //可提现金额变化前金额
        journalUser.setAccountAmountWithdrawableAfterwards(userAccountByType.getAccountAmountWithdrawable().add(changePlan.getChangeAmount())); //可提现金额变化后金额

        journalUser.setAccountAmountWithdrawedBefore(userAccountByType.getAccountAmountWithdrawed());        //已提现金额变化前金额
        journalUser.setAccountAmountWithdrawedAfterwards(userAccountByType.getAccountAmountWithdrawed());    //已提现金额变化后金额

        journalUser.setAccountAmountUndepositableBefore(userAccountByType.getAccountAmountUndepositable());     //不可提现金额变化前金额
        journalUser.setAccountAmountUndepositableAfterwards(userAccountByType.getAccountAmountUndepositable()); //不可提现金额变化后金额


        return journalUser;

    }

    private FundAccountJournal getFromFundAccountJournal(FundAccount fromFundAccount, JournalInfoVO journalVO,
                                                         FundAccountChangePlan changePlan) {


        FundAccountJournal journal = new FundAccountJournal(changePlan.getPlatNo());
        journal.setChannelId(changePlan.getChannelId());
        journal.setFundAccountChangePlanId(changePlan.getFundAccountChangePlanId());
        journal.setFundAccountId(fromFundAccount.getFundAccountId());
        journal.setJournalStatus(JournalStatus.SUCCESS);
        journal.setJournalChangeValue(changePlan.getChangeAmount());
        journal.setAccountJournalAcid(changePlan.getOrderId());

        journal.setAccountMainJournalType(journalVO.getAccountMainJournalType().getCode());
        journal.setAccountMainJournalTypeName(journalVO.getAccountMainJournalType().getName());

        journal.setAccountIncome(BigDecimal.ZERO);            //收入资金变动金额
        journal.setAccountAmountFrozen(changePlan.getChangeAmount());   //冻结资金变动收入
        journal.setAccountAmountWithdrawable(changePlan.getChangeAmount());    //可提现资金变动收入
        journal.setAccountAmountWithdrawed(BigDecimal.ZERO);  //已提现资金变动收入
        journal.setAccountAmountUndepositable(BigDecimal.ZERO);   //不可提现资金变动收入

        journal.setAccountIncomeBefore(fromFundAccount.getAccountIncome());      //收入变动前的金额
        journal.setAccountIncomeAfterwards(fromFundAccount.getAccountIncome());  //收入变动后的金额

        journal.setAccountAmountFrozenBefore(fromFundAccount.getAccountAmountFrozen());   //冻结资金变化前金额
        journal.setAccountAmountFrozenAfterwards(fromFundAccount.getAccountAmountFrozen().subtract(changePlan.getChangeAmount())); //冻结资金变化后金额

        journal.setAccountAmountWithdrawableBefore(fromFundAccount.getAccountAmountWithdrawable());   //可提现金额变化前金额
        journal.setAccountAmountWithdrawableAfterwards(fromFundAccount.getAccountAmountWithdrawable().add(changePlan.getChangeAmount())); //可提现金额变化后金额

        journal.setAccountAmountWithdrawedBefore(fromFundAccount.getAccountAmountWithdrawed());        //已提现金额变化前金额
        journal.setAccountAmountWithdrawedAfterwards(fromFundAccount.getAccountAmountWithdrawed());    //已提现金额变化后金额

        journal.setAccountAmountUndepositableBefore(fromFundAccount.getAccountAmountUndepositable());     //不可提现金额变化前金额
        journal.setAccountAmountUndepositableAfterwards(fromFundAccount.getAccountAmountUndepositable()); //不可提现金额变化后金额


        return journal;
    }
}
